package com.quick.service;

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.TargetClassAware;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.target.SingletonTargetSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName SoaMonitorServiceImpl
 * @Description TODO
 * @Author wangxin43
 * @Date 2018/7/4 下午12:16
 * @Version 1.0
 **/
@Service
public class SoaMonitorServiceImpl implements SoaMonitorService, ApplicationContextAware {
	public static final String DEFAULT_PWD = "38bced94a36a4fede2ad28eeea5a87c0";
	private Logger logger = LoggerFactory.getLogger(SoaMonitorServiceImpl.class);
	private final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
	private String pwd = "38bced94a36a4fede2ad28eeea5a87c0";
	private ApplicationContext applicationContext;

	public SoaMonitorServiceImpl() {
	}

	public void setPwd(String pwd) {
		this.pwd = pwd;
	}

	private boolean authCheck(String serviceKey, String methodKey, String pwd) {
		if (serviceKey.startsWith("spring.") && !this.pwd.equals(pwd)) {
			return false;
		} else {
			return !methodKey.contains("update") && !methodKey.contains("insert") && !methodKey.contains("delete") || this.pwd.equals(pwd);
		}
	}

	private Object getSpringBean(String beanName) {
		if (beanName.startsWith("spring.")) {
			try {
				return this.applicationContext.getBean(beanName.replace("spring.", ""));
			} catch (NoSuchBeanDefinitionException var3) {
				this.logger.warn("beanName:{} not found", beanName);
				return null;
			}
		} else {
			return null;
		}
	}

	private static Class<?> ultimateTargetClass(Object candidate) {
		Assert.notNull(candidate, "Candidate object must not be null");
		Object current = candidate;

		Class result;
		Object nested;
		for(result = null; current instanceof TargetClassAware; current = nested) {
			result = ((TargetClassAware)current).getTargetClass();
			nested = null;
			if (current instanceof Advised) {
				TargetSource targetSource = ((Advised)current).getTargetSource();
				if (targetSource instanceof SingletonTargetSource) {
					nested = ((SingletonTargetSource)targetSource).getTarget();
				}
			}
		}

		if (result == null) {
			result = AopUtils.isCglibProxy(candidate) ? candidate.getClass().getSuperclass() : candidate.getClass();
		}

		return result;
	}

	public Object invokeServiceMethod(String jsonData, String serviceKey, String methodKey, Integer methodId, String pwd) throws IllegalAccessException {
		if (!this.authCheck(serviceKey, methodKey, pwd)) {
			this.logger.warn("serviceKey:{} methodKey:{} methodId:{} jsonData:{} pwd:{}", new Object[]{serviceKey, methodKey, methodId, jsonData, pwd});
			return "auth error!";
		} else {
			Object service = this.getSpringBean(serviceKey);
			if (service == null) {
				return serviceKey + "  not found!";
			} else {
				Class<?> targetClass = ultimateTargetClass(service);
				Map<Integer, Method> methods = new HashMap();
				int index = 0;
				Method[] var10 = targetClass.getDeclaredMethods();
				int var11 = var10.length;

				for(int var12 = 0; var12 < var11; ++var12) {
					Method method = var10[var12];
					if (method.getName().equals(methodKey)) {
						methods.put(index++, method);
					}
				}

				if (methods.isEmpty()) {
					return methodKey + " is not found";
				} else if (methods.size() > 1 && methodId == null) {
					return methods;
				} else {
					if (methodId == null) {
						methodId = 0;
					}

					Method method = (Method)methods.get(methodId);
					method.setAccessible(true);
					if (method.getGenericParameterTypes() != null && method.getGenericParameterTypes().length != 0) {
						if (jsonData != null && !jsonData.isEmpty() && !jsonData.trim().isEmpty()) {
							Object[] args;
							try {
								args = JSON.parseArray(jsonData.trim(), method.getGenericParameterTypes()).toArray();
							} catch (Exception var16) {
								this.logger.error("json:{}", jsonData);
								return "json反序列化错误 :" + Arrays.toString(this.buildParameterDesc(method));
							}

							try {
								return method.invoke(service, args);
							} catch (InvocationTargetException var15) {
								Throwable throwable = var15.getTargetException();
								this.logger.error("service:{} method:{} args:{}", new Object[]{serviceKey, methodKey, args, throwable});
								StringWriter stringWriter = new StringWriter();
								throwable.printStackTrace(new PrintWriter(stringWriter));
								return stringWriter.toString();
							}
						} else {
							return "入参错误 :" + Arrays.toString(this.buildParameterDesc(method));
						}
					} else {
						try {
							return method.invoke(service);
						} catch (InvocationTargetException var17) {
							Throwable throwable = var17.getTargetException();
							this.logger.error("service:{} method:{}", new Object[]{serviceKey, methodKey, throwable});
							StringWriter stringWriter = new StringWriter();
							throwable.printStackTrace(new PrintWriter(stringWriter));
							return stringWriter.toString();
						}
					}
				}
			}
		}
	}

	private String[] buildParameterDesc(Method method) {
		Type[] parameterTypes = method.getGenericParameterTypes();
		String[] descs = this.parameterNameDiscoverer.getParameterNames(method);
		String[] argDescs = new String[method.getGenericParameterTypes().length];

		for(int i = 0; i < argDescs.length; ++i) {
			argDescs[i] = parameterTypes[i] + " " + descs[i];
		}

		return argDescs;
	}

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}
}
